Installing required packages
library(rtweet)
package 㤼㸱rtweet㤼㸲 was built under R version 4.0.4
library(dplyr)
library(tidyr)
library(tidytext)
package 㤼㸱tidytext㤼㸲 was built under R version 4.0.4
library(ggplot.multistats)
package 㤼㸱ggplot.multistats㤼㸲 was built under R version 4.0.4
library(ggplot2)
library(forestmangr)
package 㤼㸱forestmangr㤼㸲 was built under R version 4.0.4
Attaching package: 㤼㸱forestmangr㤼㸲
The following object is masked from 㤼㸱package:data.table㤼㸲:
:=
library(syuzhet)
package 㤼㸱syuzhet㤼㸲 was built under R version 4.0.4
Attaching package: 㤼㸱syuzhet㤼㸲
The following object is masked from 㤼㸱package:rtweet㤼㸲:
get_tokens
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
-- Attaching packages ----------------------------- tidyverse 1.3.0 --
<U+221A> tibble 3.0.6 <U+221A> stringr 1.4.0
<U+221A> readr 1.4.0 <U+221A> forcats 0.5.1
<U+221A> purrr 0.3.4
-- Conflicts -------------------------------- tidyverse_conflicts() --
x dplyr::between() masks data.table::between()
x dplyr::filter() masks stats::filter()
x dplyr::first() masks data.table::first()
x purrr::flatten() masks rtweet::flatten()
x dplyr::lag() masks stats::lag()
x dplyr::last() masks data.table::last()
x purrr::transpose() masks data.table::transpose()
library(readxl) # read excel
library(tibble) # tobble dataframe
library(dplyr) # piping
library(stringr) # character manipulation
library(tidytext)
library(tokenizers)
package 㤼㸱tokenizers㤼㸲 was built under R version 4.0.4
library(stopwords)
package 㤼㸱stopwords㤼㸲 was built under R version 4.0.4
library(tidyverse)
library(lobstr)
package 㤼㸱lobstr㤼㸲 was built under R version 4.0.4
Loading the data
blm2020<-read.csv("https://www.dropbox.com/s/7ywlwmynitdb2rr/BLM2020_Dataset.csv?dl=1", sep = '\t', na.strings = c("", "NA"))
blm2021<-read.csv("https://www.dropbox.com/s/glpv88v6hb0u4j1/BLM2021_Dataset.csv?dl=1", sep = '\t', na.strings = c("", "NA"))
Show the ratio of replies, retweets and likes
#Retweets
blm2020_retweets<-blm2020[blm2020$retweet > 1, ]
blm2020_retweets
blm2021_retweets<-blm2021[blm2021$retweet > 1, ]
blm2021_retweets
NA
#Likes
blm2020_likes<-blm2020[blm2020$like >1, ]
blm2020_likes
blm2021_likes<-blm2021[blm2021$like >1, ]
blm2021_likes
NA
#Replies
blm2020_replies<-blm2020[blm2020$reply >1, ]
blm2020_replies
blm2021_replies<-blm2021[blm2021$reply >1, ]
blm2021_replies
NA
Creating a data frame
data2020<-data.frame(category=c("Retweets", "Likes", "Replies"), count=c(2, 5, 3))
data2020
data2021<-data.frame(category=c("Retweets", "Likes", "Replies"), count=c(707, 1738, 574))
data2021
Visualizing the data 2020
#Adding columns
data2020$fraction = data2020$count / sum(data2020$count)
data2020$percentage = data2020$count / sum(data2020$count) * 100
data2020$ymax = cumsum(data2020$fraction)
data2020$ymin = c(0, head(data2020$ymax, n=-1))
#Rounding up
data2020 <- round_df(data2020, 2)
# Specify what the legend should say
Type_of_Tweet <- paste(data2020$category, data2020$percentage, "%")
ggplot(data2020, aes(ymax=ymax, ymin=ymin, xmax=4, xmin=3, fill=Type_of_Tweet)) +
geom_rect() +
coord_polar(theta="y") +
xlim(c(2, 4)) +
theme_void() +
theme(legend.position = "right")

Visualizing the data 2021
#Adding columns
data2021$fraction = data2021$count / sum(data2021$count)
data2021$percentage = data2021$count / sum(data2021$count) * 100
data2021$ymax = cumsum(data2021$fraction)
data2021$ymin = c(0, head(data2021$ymax, n=-1))
#Rounding up
data2021 <- round_df(data2021, 2)
# Specify what the legend should say
Type_of_Tweet <- paste(data2020$category, data2021$percentage, "%")
ggplot(data2021, aes(ymax=ymax, ymin=ymin, xmax=4, xmin=3, fill=Type_of_Tweet)) +
geom_rect() +
coord_polar(theta="y") +
xlim(c(2, 4)) +
theme_void() +
theme(legend.position = "right")

Most frequent words in the 2020 tweets
Lets see which words are more frequently used in the posts of 2020
blm2020$text <- gsub("@\\S*", "", blm2020$text)
blm2020$text <- gsub("amp", "", blm2020$text)
blm2020$text <- gsub("[\r\n]", "", blm2020$text)
blm2020$text <- gsub("[[:punct:]]", "", blm2020$text)
#removing stop words from the text - so the most frequent words are not or at and
tweets <- blm2020 %>%
select(text) %>%
unnest_tokens(word, text)
tweets <- tweets %>%
anti_join(get_stopwords(language = "nl", source = "snowball"))
Joining, by = "word"
#making a histogram
tweets %>% # gives you a bar chart of the most frequent words found in the tweets
count(word, sort = TRUE) %>%
top_n(15) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(x = word, y = n)) +
geom_col() +
xlab(NULL) +
coord_flip() +
labs(y = "Count",
x = "Unique words",
title = "Most frequent words found in the tweets of #BLM",
subtitle = "Stop words removed from the list")
Selecting by n

Most frequent words in the 2021 tweets Lets now see which words are more frequently used in the posts of 2021
blm2021$text <- gsub("@\\S*", "", blm2021$text)
blm2021$text <- gsub("amp", "", blm2021$text)
blm2021$text <- gsub("[\r\n]", "", blm2021$text)
blm2021$text <- gsub("[[:punct:]]", "", blm2021$text)
#removing stop words from the text - so the most frequent words are not or at and
tweets1 <- blm2021 %>%
select(text) %>%
unnest_tokens(word, text)
tweets1 <- tweets1 %>%
anti_join(get_stopwords(language = "nl", source = "snowball"))
Joining, by = "word"
#making a histogram
tweets1 %>% # gives you a bar chart of the most frequent words found in the tweets
count(word, sort = TRUE) %>%
top_n(15) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(x = word, y = n)) +
geom_col() +
xlab(NULL) +
coord_flip() +
labs(y = "Count",
x = "Unique words",
title = "Most frequent words found in the tweets of #BLM 2021",
subtitle = "Stop words removed from the list")
Selecting by n

Sentiment analysis for positive or negative tweets 2020
# Converting tweets to ASCII to trackle strange characters
tweets_sentiment <- iconv(tweets, from="UTF-8", to="ASCII", sub="")
# removing retweets, in case needed
tweets_sentiment <-gsub("(RT|via)((?:\\b\\w*@\\w+)+)","",tweets_sentiment)
# removing mentions, in case needed
tweets_sentiment <-gsub("@\\w+","",tweets_sentiment)
ew_sentiment<-get_nrc_sentiment((tweets_sentiment))
sentimentscores<-data.frame(colSums(ew_sentiment[,]))
names(sentimentscores) <- "Score"
sentimentscores <- cbind("sentiment"=rownames(sentimentscores),sentimentscores)
rownames(sentimentscores) <- NULL
ggplot(data=sentimentscores,aes(x=sentiment,y=Score))+
geom_bar(aes(fill=sentiment),stat = "identity")+
theme(legend.position="none")+
xlab("Sentiments")+ylab("Scores")+
ggtitle("Total sentiment based on scores 2020")+
theme_minimal()

Sentiment analysis for positive or negative tweets 2021
# Converting tweets to ASCII to trackle strange characters
tweets_sentiment_1 <- iconv(tweets, from="UTF-8", to="ASCII", sub="")
# removing retweets, in case needed
tweets_sentiment_1 <-gsub("(RT|via)((?:\\b\\w*@\\w+)+)","",tweets_sentiment_1)
# removing mentions, in case needed
tweets_sentiment_1 <-gsub("@\\w+","",tweets_sentiment_1)
ew_sentiment<-get_nrc_sentiment((tweets_sentiment))
sentimentscores<-data.frame(colSums(ew_sentiment[,]))
names(sentimentscores) <- "Score"
sentimentscores <- cbind("sentiment"=rownames(sentimentscores),sentimentscores)
rownames(sentimentscores) <- NULL
ggplot(data=sentimentscores,aes(x=sentiment,y=Score))+
geom_bar(aes(fill=sentiment),stat = "identity")+
theme(legend.position="none")+
xlab("Sentiments")+ylab("Scores")+
ggtitle("Total sentiment based on scores 2021")+
theme_minimal()

Visualizing the variables in the dataset 2020
#Making a ggplot
#ploting retweets by location
ggplot(data = blm2020) +
geom_point(mapping = aes(x = retweet, y = location))

#ploting retweets by datetime
#need to separate date from time
ggplot(data = blm2020) +
geom_point(mapping = aes(x = retweet, y = datetime), color = "blue")
Graphing users amount of followers and user amount friends
ggplot(data = blm2020) +
geom_point(mapping = aes(x = user_amount_followers, y = user_amount_friends), color = "blue")

Graphing users amount status by location To check how much users Tweet by Location
ggplot(data = blm2020) +
geom_point(mapping = aes(x = user_amount_status, y = location), color = "blue")

#Filter if parties are mentioned in 2020
state.name[grep ("VVD", "PvdA", "PVV", "CDA", "SP", "CU", "PvdD", state.name)]
character(0)
Visualizing the variables in the dataset 2021 ####needs amendments!!!!
#Making a ggplot
#ploting retweets by location
ggplot(data = blm2021) +
geom_point(mapping = aes(x = retweet, y = location))

#ploting retweets by datetime
#need to separate date from time
ggplot(data = blm2021) +
geom_point(mapping = aes(x = retweet, y = datetime), color = "blue")

Graphing users amount of followers and user amount friends
ggplot(data = blm2021) +
geom_point(mapping = aes(x = user_amount_followers, y = user_amount_friends), color = "blue")

Graphing users amount status by location To check how much users Tweet by Location
ggplot(data = blm2021) +
geom_point(mapping = aes(x = user_amount_status, y = location), color = "blue")

#Filter if parties are mentioned in 2021
state.name[grep ("VVD", "PvdA", "PVV", "CDA", "SP", "CU", "PvdD", state.name)]
#How can we measure how many tweets are made by location?
references: 1. https://towardsdatascience.com/a-guide-to-mining-and-analysing-tweets-with-r-2f56818fdd16 2. https://ourcodingclub.github.io/tutorials/time/ 3. https://cran.r-project.org/web/packages/stopwords/stopwords.pdf 4. https://www.dummies.com/programming/r/how-to-search-for-individual-words-in-r/ 5. https://r4ds.had.co.nz/data-visualisation.html
sum(""(BLM2020))
sum(is.na(BLM2020$Datetime))
sum(is.na(BLM2020$Text))
sum(is.na(BLM2020$Tweet.Id))
sum(is.na(BLM2020$Username))
sum(is.na(BLM2020$Location))
sum(is.na(BLM2020$Location))
summary(BLM2020)
summary(BLM2020$Location)
regression retweets
retweets_glm <-glm(Retweet ~ ., data=BLM2020)
One way ANOVA
#removing html tags from the source_tweet variable
library(textclean)
html_tags <- c(
"<bold>Random</bold> text with symbols: < > & " '",
"<p>More text</p> ¢ £ ¥ € © ®"
)
blm2020$source_tweet<- replace_html(blm2020$source_tweet)
running the test
library(car)
one.way <- aov(source_tweet ~ ., data = blm2020)
Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) :
contrasts can be applied only to factors with 2 or more levels
#Graphs for every variable #Graph on user ammount of followers #Check if a user has made multiple tweets - how can we do that? #Filter if parties are mentioned
LS0tDQp0aXRsZTogIkJMTSBkYXRhIGFuYWx5c2lzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KKkluc3RhbGxpbmcgcmVxdWlyZWQgcGFja2FnZXMqDQoNCmBgYHtyfQ0KbGlicmFyeShydHdlZXQpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkodGlkeXRleHQpDQpsaWJyYXJ5KGdncGxvdC5tdWx0aXN0YXRzKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShmb3Jlc3RtYW5ncikNCmxpYnJhcnkoc3l1emhldCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkeGwpICMgcmVhZCBleGNlbA0KbGlicmFyeSh0aWJibGUpICMgdG9iYmxlIGRhdGFmcmFtZQ0KbGlicmFyeShkcGx5cikgIyBwaXBpbmcNCmxpYnJhcnkoc3RyaW5ncikgIyBjaGFyYWN0ZXIgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeSh0b2tlbml6ZXJzKQ0KbGlicmFyeShzdG9wd29yZHMpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkobG9ic3RyKQ0KDQpgYGANCipMb2FkaW5nIHRoZSBkYXRhKg0KDQpgYGB7cn0NCmJsbTIwMjA8LXJlYWQuY3N2KCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zLzd5d2x3bXluaXRkYjJyci9CTE0yMDIwX0RhdGFzZXQuY3N2P2RsPTEiLCBzZXAgPSAnXHQnLCBuYS5zdHJpbmdzID0gYygiIiwgIk5BIikpDQoNCmJsbTIwMjE8LXJlYWQuY3N2KCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zL2dscHY4OHY2aGIwdTRqMS9CTE0yMDIxX0RhdGFzZXQuY3N2P2RsPTEiLCBzZXAgPSAnXHQnLCBuYS5zdHJpbmdzID0gYygiIiwgIk5BIikpDQpgYGANCg0KDQoqU2hvdyB0aGUgcmF0aW8gb2YgcmVwbGllcywgcmV0d2VldHMgYW5kIGxpa2VzKg0KDQpgYGB7cn0NCiNSZXR3ZWV0cw0KYmxtMjAyMF9yZXR3ZWV0czwtYmxtMjAyMFtibG0yMDIwJHJldHdlZXQgPiAxLCBdDQpibG0yMDIwX3JldHdlZXRzDQpibG0yMDIxX3JldHdlZXRzPC1ibG0yMDIxW2JsbTIwMjEkcmV0d2VldCA+IDEsIF0NCmJsbTIwMjFfcmV0d2VldHMNCg0KYGBgDQoNCmBgYHtyfQ0KI0xpa2VzDQpibG0yMDIwX2xpa2VzPC1ibG0yMDIwW2JsbTIwMjAkbGlrZSA+MSwgXQ0KYmxtMjAyMF9saWtlcw0KYmxtMjAyMV9saWtlczwtYmxtMjAyMVtibG0yMDIxJGxpa2UgPjEsIF0NCmJsbTIwMjFfbGlrZXMNCg0KYGBgDQoNCg0KYGBge3J9DQojUmVwbGllcw0KYmxtMjAyMF9yZXBsaWVzPC1ibG0yMDIwW2JsbTIwMjAkcmVwbHkgPjEsIF0NCmJsbTIwMjBfcmVwbGllcw0KYmxtMjAyMV9yZXBsaWVzPC1ibG0yMDIxW2JsbTIwMjEkcmVwbHkgPjEsIF0NCmJsbTIwMjFfcmVwbGllcw0KDQpgYGANCipDcmVhdGluZyBhIGRhdGEgZnJhbWUqDQoNCmBgYHtyfQ0KDQpkYXRhMjAyMDwtZGF0YS5mcmFtZShjYXRlZ29yeT1jKCJSZXR3ZWV0cyIsICJMaWtlcyIsICJSZXBsaWVzIiksIGNvdW50PWMoMiwgNSwgMykpDQpkYXRhMjAyMA0KZGF0YTIwMjE8LWRhdGEuZnJhbWUoY2F0ZWdvcnk9YygiUmV0d2VldHMiLCAiTGlrZXMiLCAiUmVwbGllcyIpLCBjb3VudD1jKDcwNywgMTczOCwgNTc0KSkNCmRhdGEyMDIxDQpgYGANCg0KKlZpc3VhbGl6aW5nIHRoZSBkYXRhIDIwMjAqDQoNCmBgYHtyfQ0KI0FkZGluZyBjb2x1bW5zDQpkYXRhMjAyMCRmcmFjdGlvbiA9IGRhdGEyMDIwJGNvdW50IC8gc3VtKGRhdGEyMDIwJGNvdW50KQ0KZGF0YTIwMjAkcGVyY2VudGFnZSA9IGRhdGEyMDIwJGNvdW50IC8gc3VtKGRhdGEyMDIwJGNvdW50KSAqIDEwMA0KZGF0YTIwMjAkeW1heCA9IGN1bXN1bShkYXRhMjAyMCRmcmFjdGlvbikNCmRhdGEyMDIwJHltaW4gPSBjKDAsIGhlYWQoZGF0YTIwMjAkeW1heCwgbj0tMSkpDQoNCiNSb3VuZGluZyB1cA0KDQpkYXRhMjAyMCA8LSByb3VuZF9kZihkYXRhMjAyMCwgMikNCg0KIyBTcGVjaWZ5IHdoYXQgdGhlIGxlZ2VuZCBzaG91bGQgc2F5DQpUeXBlX29mX1R3ZWV0IDwtIHBhc3RlKGRhdGEyMDIwJGNhdGVnb3J5LCBkYXRhMjAyMCRwZXJjZW50YWdlLCAiJSIpDQoNCmdncGxvdChkYXRhMjAyMCwgYWVzKHltYXg9eW1heCwgeW1pbj15bWluLCB4bWF4PTQsIHhtaW49MywgZmlsbD1UeXBlX29mX1R3ZWV0KSkgKw0KICBnZW9tX3JlY3QoKSArDQogIGNvb3JkX3BvbGFyKHRoZXRhPSJ5IikgKyANCiAgeGxpbShjKDIsIDQpKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpDQoNCmBgYA0KKlZpc3VhbGl6aW5nIHRoZSBkYXRhIDIwMjEqDQoNCmBgYHtyfQ0KDQojQWRkaW5nIGNvbHVtbnMNCmRhdGEyMDIxJGZyYWN0aW9uID0gZGF0YTIwMjEkY291bnQgLyBzdW0oZGF0YTIwMjEkY291bnQpDQpkYXRhMjAyMSRwZXJjZW50YWdlID0gZGF0YTIwMjEkY291bnQgLyBzdW0oZGF0YTIwMjEkY291bnQpICogMTAwDQpkYXRhMjAyMSR5bWF4ID0gY3Vtc3VtKGRhdGEyMDIxJGZyYWN0aW9uKQ0KZGF0YTIwMjEkeW1pbiA9IGMoMCwgaGVhZChkYXRhMjAyMSR5bWF4LCBuPS0xKSkNCg0KI1JvdW5kaW5nIHVwDQoNCmRhdGEyMDIxIDwtIHJvdW5kX2RmKGRhdGEyMDIxLCAyKQ0KDQojIFNwZWNpZnkgd2hhdCB0aGUgbGVnZW5kIHNob3VsZCBzYXkNClR5cGVfb2ZfVHdlZXQgPC0gcGFzdGUoZGF0YTIwMjAkY2F0ZWdvcnksIGRhdGEyMDIxJHBlcmNlbnRhZ2UsICIlIikNCg0KZ2dwbG90KGRhdGEyMDIxLCBhZXMoeW1heD15bWF4LCB5bWluPXltaW4sIHhtYXg9NCwgeG1pbj0zLCBmaWxsPVR5cGVfb2ZfVHdlZXQpKSArDQogIGdlb21fcmVjdCgpICsNCiAgY29vcmRfcG9sYXIodGhldGE9InkiKSArIA0KICB4bGltKGMoMiwgNCkpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikNCg0KYGBgDQoNCg0KDQoNCipNb3N0IGZyZXF1ZW50IHdvcmRzIGluIHRoZSAyMDIwIHR3ZWV0cyoNCg0KTGV0cyBzZWUgd2hpY2ggd29yZHMgYXJlIG1vcmUgZnJlcXVlbnRseSB1c2VkIGluIHRoZSBwb3N0cyBvZiAyMDIwDQoNCmBgYHtyfQ0KYmxtMjAyMCR0ZXh0IDwtIGdzdWIoIkBcXFMqIiwgIiIsIGJsbTIwMjAkdGV4dCkNCmJsbTIwMjAkdGV4dCA8LSBnc3ViKCJhbXAiLCAiIiwgYmxtMjAyMCR0ZXh0KSANCmJsbTIwMjAkdGV4dCA8LSBnc3ViKCJbXHJcbl0iLCAiIiwgYmxtMjAyMCR0ZXh0KQ0KYmxtMjAyMCR0ZXh0IDwtIGdzdWIoIltbOnB1bmN0Ol1dIiwgIiIsIGJsbTIwMjAkdGV4dCkNCg0KYGBgDQoNCg0KYGBge3J9DQoNCiNyZW1vdmluZyBzdG9wIHdvcmRzIGZyb20gdGhlIHRleHQgLSBzbyB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcyBhcmUgbm90IG9yIGF0IGFuZA0KDQp0d2VldHMgPC0gYmxtMjAyMCAlPiUNCiAgc2VsZWN0KHRleHQpICU+JQ0KICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpDQp0d2VldHMgPC0gdHdlZXRzICU+JQ0KICBhbnRpX2pvaW4oZ2V0X3N0b3B3b3JkcyhsYW5ndWFnZSA9ICJubCIsIHNvdXJjZSA9ICJzbm93YmFsbCIpKQ0KDQpgYGANCg0KYGBge3J9DQojbWFraW5nIGEgaGlzdG9ncmFtDQp0d2VldHMgJT4lICMgZ2l2ZXMgeW91IGEgYmFyIGNoYXJ0IG9mIHRoZSBtb3N0IGZyZXF1ZW50IHdvcmRzIGZvdW5kIGluIHRoZSB0d2VldHMNCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpICU+JQ0KICB0b3BfbigxNSkgJT4lDQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IHdvcmQsIHkgPSBuKSkgKw0KICBnZW9tX2NvbCgpICsNCiAgeGxhYihOVUxMKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnMoeSA9ICJDb3VudCIsDQogICAgICAgeCA9ICJVbmlxdWUgd29yZHMiLA0KICAgICAgIHRpdGxlID0gIk1vc3QgZnJlcXVlbnQgd29yZHMgZm91bmQgaW4gdGhlIHR3ZWV0cyBvZiAjQkxNIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJTdG9wIHdvcmRzIHJlbW92ZWQgZnJvbSB0aGUgbGlzdCIpDQpgYGANCipNb3N0IGZyZXF1ZW50IHdvcmRzIGluIHRoZSAyMDIxIHR3ZWV0cyoNCkxldHMgbm93IHNlZSB3aGljaCB3b3JkcyBhcmUgbW9yZSBmcmVxdWVudGx5IHVzZWQgaW4gdGhlIHBvc3RzIG9mIDIwMjEgDQoNCmBgYHtyfQ0KYmxtMjAyMSR0ZXh0IDwtIGdzdWIoIkBcXFMqIiwgIiIsIGJsbTIwMjEkdGV4dCkNCmJsbTIwMjEkdGV4dCA8LSBnc3ViKCJhbXAiLCAiIiwgYmxtMjAyMSR0ZXh0KSANCmJsbTIwMjEkdGV4dCA8LSBnc3ViKCJbXHJcbl0iLCAiIiwgYmxtMjAyMSR0ZXh0KQ0KYmxtMjAyMSR0ZXh0IDwtIGdzdWIoIltbOnB1bmN0Ol1dIiwgIiIsIGJsbTIwMjEkdGV4dCkNCg0KYGBgDQoNCg0KYGBge3J9DQoNCiNyZW1vdmluZyBzdG9wIHdvcmRzIGZyb20gdGhlIHRleHQgLSBzbyB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcyBhcmUgbm90IG9yIGF0IGFuZA0KDQp0d2VldHMxIDwtIGJsbTIwMjEgJT4lDQogIHNlbGVjdCh0ZXh0KSAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KQ0KdHdlZXRzMSA8LSB0d2VldHMxICU+JQ0KICBhbnRpX2pvaW4oZ2V0X3N0b3B3b3JkcyhsYW5ndWFnZSA9ICJubCIsIHNvdXJjZSA9ICJzbm93YmFsbCIpKQ0KDQpgYGANCg0KYGBge3J9DQojbWFraW5nIGEgaGlzdG9ncmFtDQp0d2VldHMxICU+JSAjIGdpdmVzIHlvdSBhIGJhciBjaGFydCBvZiB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcyBmb3VuZCBpbiB0aGUgdHdlZXRzDQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUNCiAgdG9wX24oMTUpICU+JQ0KICBtdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB3b3JkLCB5ID0gbikpICsNCiAgZ2VvbV9jb2woKSArDQogIHhsYWIoTlVMTCkgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHkgPSAiQ291bnQiLA0KICAgICAgIHggPSAiVW5pcXVlIHdvcmRzIiwNCiAgICAgICB0aXRsZSA9ICJNb3N0IGZyZXF1ZW50IHdvcmRzIGZvdW5kIGluIHRoZSB0d2VldHMgb2YgI0JMTSAyMDIxIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJTdG9wIHdvcmRzIHJlbW92ZWQgZnJvbSB0aGUgbGlzdCIpDQpgYGANCg0KKlNlbnRpbWVudCBhbmFseXNpcyBmb3IgcG9zaXRpdmUgb3IgbmVnYXRpdmUgdHdlZXRzIDIwMjAqDQoNCmBgYHtyfQ0KIyBDb252ZXJ0aW5nIHR3ZWV0cyB0byBBU0NJSSB0byB0cmFja2xlIHN0cmFuZ2UgY2hhcmFjdGVycw0KdHdlZXRzX3NlbnRpbWVudCA8LSBpY29udih0d2VldHMsIGZyb209IlVURi04IiwgdG89IkFTQ0lJIiwgc3ViPSIiKQ0KDQojIHJlbW92aW5nIHJldHdlZXRzLCBpbiBjYXNlIG5lZWRlZCANCnR3ZWV0c19zZW50aW1lbnQgPC1nc3ViKCIoUlR8dmlhKSgoPzpcXGJcXHcqQFxcdyspKykiLCIiLHR3ZWV0c19zZW50aW1lbnQpDQoNCiMgcmVtb3ZpbmcgbWVudGlvbnMsIGluIGNhc2UgbmVlZGVkDQp0d2VldHNfc2VudGltZW50IDwtZ3N1YigiQFxcdysiLCIiLHR3ZWV0c19zZW50aW1lbnQpDQpld19zZW50aW1lbnQ8LWdldF9ucmNfc2VudGltZW50KCh0d2VldHNfc2VudGltZW50KSkNCnNlbnRpbWVudHNjb3JlczwtZGF0YS5mcmFtZShjb2xTdW1zKGV3X3NlbnRpbWVudFssXSkpDQpuYW1lcyhzZW50aW1lbnRzY29yZXMpIDwtICJTY29yZSINCnNlbnRpbWVudHNjb3JlcyA8LSBjYmluZCgic2VudGltZW50Ij1yb3duYW1lcyhzZW50aW1lbnRzY29yZXMpLHNlbnRpbWVudHNjb3JlcykNCnJvd25hbWVzKHNlbnRpbWVudHNjb3JlcykgPC0gTlVMTA0KZ2dwbG90KGRhdGE9c2VudGltZW50c2NvcmVzLGFlcyh4PXNlbnRpbWVudCx5PVNjb3JlKSkrDQogIGdlb21fYmFyKGFlcyhmaWxsPXNlbnRpbWVudCksc3RhdCA9ICJpZGVudGl0eSIpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSsNCiAgeGxhYigiU2VudGltZW50cyIpK3lsYWIoIlNjb3JlcyIpKw0KICBnZ3RpdGxlKCJUb3RhbCBzZW50aW1lbnQgYmFzZWQgb24gc2NvcmVzIDIwMjAiKSsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KKlNlbnRpbWVudCBhbmFseXNpcyBmb3IgcG9zaXRpdmUgb3IgbmVnYXRpdmUgdHdlZXRzIDIwMjEqDQoNCmBgYHtyfQ0KIyBDb252ZXJ0aW5nIHR3ZWV0cyB0byBBU0NJSSB0byB0cmFja2xlIHN0cmFuZ2UgY2hhcmFjdGVycw0KdHdlZXRzX3NlbnRpbWVudF8xIDwtIGljb252KHR3ZWV0cywgZnJvbT0iVVRGLTgiLCB0bz0iQVNDSUkiLCBzdWI9IiIpDQoNCiMgcmVtb3ZpbmcgcmV0d2VldHMsIGluIGNhc2UgbmVlZGVkIA0KdHdlZXRzX3NlbnRpbWVudF8xIDwtZ3N1YigiKFJUfHZpYSkoKD86XFxiXFx3KkBcXHcrKSspIiwiIix0d2VldHNfc2VudGltZW50XzEpDQoNCiMgcmVtb3ZpbmcgbWVudGlvbnMsIGluIGNhc2UgbmVlZGVkDQp0d2VldHNfc2VudGltZW50XzEgPC1nc3ViKCJAXFx3KyIsIiIsdHdlZXRzX3NlbnRpbWVudF8xKQ0KZXdfc2VudGltZW50PC1nZXRfbnJjX3NlbnRpbWVudCgodHdlZXRzX3NlbnRpbWVudCkpDQpzZW50aW1lbnRzY29yZXM8LWRhdGEuZnJhbWUoY29sU3Vtcyhld19zZW50aW1lbnRbLF0pKQ0KbmFtZXMoc2VudGltZW50c2NvcmVzKSA8LSAiU2NvcmUiDQpzZW50aW1lbnRzY29yZXMgPC0gY2JpbmQoInNlbnRpbWVudCI9cm93bmFtZXMoc2VudGltZW50c2NvcmVzKSxzZW50aW1lbnRzY29yZXMpDQpyb3duYW1lcyhzZW50aW1lbnRzY29yZXMpIDwtIE5VTEwNCmdncGxvdChkYXRhPXNlbnRpbWVudHNjb3JlcyxhZXMoeD1zZW50aW1lbnQseT1TY29yZSkpKw0KICBnZW9tX2JhcihhZXMoZmlsbD1zZW50aW1lbnQpLHN0YXQgPSAiaWRlbnRpdHkiKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikrDQogIHhsYWIoIlNlbnRpbWVudHMiKSt5bGFiKCJTY29yZXMiKSsNCiAgZ2d0aXRsZSgiVG90YWwgc2VudGltZW50IGJhc2VkIG9uIHNjb3JlcyAyMDIxIikrDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KDQoNCipWaXN1YWxpemluZyB0aGUgdmFyaWFibGVzIGluIHRoZSBkYXRhc2V0IDIwMjAqDQoNCmBgYHtyfQ0KI01ha2luZyBhIGdncGxvdA0KI3Bsb3RpbmcgcmV0d2VldHMgYnkgbG9jYXRpb24NCmdncGxvdChkYXRhID0gYmxtMjAyMCkgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSByZXR3ZWV0LCB5ID0gbG9jYXRpb24pKQ0KYGBgDQpgYGB7cn0NCiNwbG90aW5nIHJldHdlZXRzIGJ5IGRhdGV0aW1lDQojbmVlZCB0byBzZXBhcmF0ZSBkYXRlIGZyb20gdGltZQ0KZ2dwbG90KGRhdGEgPSBibG0yMDIwKSArIA0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHJldHdlZXQsIHkgPSBkYXRldGltZSksIGNvbG9yID0gImJsdWUiKQ0KDQpgYGANCipHcmFwaGluZyB1c2VycyBhbW91bnQgb2YgZm9sbG93ZXJzIGFuZCB1c2VyIGFtb3VudCBmcmllbmRzKg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gYmxtMjAyMCkgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSB1c2VyX2Ftb3VudF9mb2xsb3dlcnMsIHkgPSB1c2VyX2Ftb3VudF9mcmllbmRzKSwgY29sb3IgPSAiYmx1ZSIpDQoNCmBgYA0KDQoqR3JhcGhpbmcgdXNlcnMgYW1vdW50IHN0YXR1cyBieSBsb2NhdGlvbioNClRvIGNoZWNrIGhvdyBtdWNoIHVzZXJzIFR3ZWV0IGJ5IExvY2F0aW9uDQpgYGB7cn0NCmdncGxvdChkYXRhID0gYmxtMjAyMCkgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSB1c2VyX2Ftb3VudF9zdGF0dXMsIHkgPSBsb2NhdGlvbiksIGNvbG9yID0gImJsdWUiKQ0KYGBgDQojRmlsdGVyIGlmIHBhcnRpZXMgYXJlIG1lbnRpb25lZCBpbiAyMDIwDQoNCmBgYHtyfQ0Kc3RhdGUubmFtZVtncmVwICgiVlZEIiwgIlB2ZEEiLCAiUFZWIiwgIkNEQSIsICJTUCIsICJDVSIsICJQdmREIiwgc3RhdGUubmFtZSldDQpgYGANCg0KDQoqVmlzdWFsaXppbmcgdGhlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCAyMDIxKg0KIyMjI25lZWRzIGFtZW5kbWVudHMhISEhDQpgYGB7cn0NCiNNYWtpbmcgYSBnZ3Bsb3QNCiNwbG90aW5nIHJldHdlZXRzIGJ5IGxvY2F0aW9uDQpnZ3Bsb3QoZGF0YSA9IGJsbTIwMjEpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gcmV0d2VldCwgeSA9IGxvY2F0aW9uKSkNCmBgYA0KYGBge3J9DQojcGxvdGluZyByZXR3ZWV0cyBieSBkYXRldGltZQ0KI25lZWQgdG8gc2VwYXJhdGUgZGF0ZSBmcm9tIHRpbWUNCmdncGxvdChkYXRhID0gYmxtMjAyMSkgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSByZXR3ZWV0LCB5ID0gZGF0ZXRpbWUpLCBjb2xvciA9ICJibHVlIikNCg0KYGBgDQoqR3JhcGhpbmcgdXNlcnMgYW1vdW50IG9mIGZvbGxvd2VycyBhbmQgdXNlciBhbW91bnQgZnJpZW5kcyoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGJsbTIwMjEpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gdXNlcl9hbW91bnRfZm9sbG93ZXJzLCB5ID0gdXNlcl9hbW91bnRfZnJpZW5kcyksIGNvbG9yID0gImJsdWUiKQ0KDQpgYGANCg0KKkdyYXBoaW5nIHVzZXJzIGFtb3VudCBzdGF0dXMgYnkgbG9jYXRpb24qDQpUbyBjaGVjayBob3cgbXVjaCB1c2VycyBUd2VldCBieSBMb2NhdGlvbg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGJsbTIwMjEpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gdXNlcl9hbW91bnRfc3RhdHVzLCB5ID0gbG9jYXRpb24pLCBjb2xvciA9ICJibHVlIikNCmBgYA0KI0ZpbHRlciBpZiBwYXJ0aWVzIGFyZSBtZW50aW9uZWQgaW4gMjAyMQ0KDQpgYGB7cn0NCnN0YXRlLm5hbWVbZ3JlcCAoIlZWRCIsICJQdmRBIiwgIlBWViIsICJDREEiLCAiU1AiLCAiQ1UiLCAiUHZkRCIsIHN0YXRlLm5hbWUpXQ0KYGBgDQoNCiNIb3cgY2FuIHdlIG1lYXN1cmUgaG93IG1hbnkgdHdlZXRzIGFyZSBtYWRlIGJ5IGxvY2F0aW9uPw0KDQpyZWZlcmVuY2VzOg0KMS4gaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL2EtZ3VpZGUtdG8tbWluaW5nLWFuZC1hbmFseXNpbmctdHdlZXRzLXdpdGgtci0yZjU2ODE4ZmRkMTYNCjIuIGh0dHBzOi8vb3VyY29kaW5nY2x1Yi5naXRodWIuaW8vdHV0b3JpYWxzL3RpbWUvDQozLiBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc3RvcHdvcmRzL3N0b3B3b3Jkcy5wZGYNCjQuIGh0dHBzOi8vd3d3LmR1bW1pZXMuY29tL3Byb2dyYW1taW5nL3IvaG93LXRvLXNlYXJjaC1mb3ItaW5kaXZpZHVhbC13b3Jkcy1pbi1yLw0KNS4gaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRhLXZpc3VhbGlzYXRpb24uaHRtbA0KYGBge3J9DQpzdW0oIiIoQkxNMjAyMCkpDQpzdW0oaXMubmEoQkxNMjAyMCREYXRldGltZSkpDQpzdW0oaXMubmEoQkxNMjAyMCRUZXh0KSkNCnN1bShpcy5uYShCTE0yMDIwJFR3ZWV0LklkKSkNCnN1bShpcy5uYShCTE0yMDIwJFVzZXJuYW1lKSkNCnN1bShpcy5uYShCTE0yMDIwJExvY2F0aW9uKSkNCnN1bShpcy5uYShCTE0yMDIwJExvY2F0aW9uKSkNCnN1bW1hcnkoQkxNMjAyMCkNCnN1bW1hcnkoQkxNMjAyMCRMb2NhdGlvbikNCmBgYA0KDQoqcmVncmVzc2lvbiByZXR3ZWV0cyoNCg0KYGBge3J9DQpyZXR3ZWV0c19nbG0gPC1nbG0oUmV0d2VldCB+IC4sIGRhdGE9QkxNMjAyMCkNCmBgYA0KDQoqT25lIHdheSBBTk9WQSoNCg0KYGBge3J9DQojcmVtb3ZpbmcgaHRtbCB0YWdzIGZyb20gdGhlIHNvdXJjZV90d2VldCB2YXJpYWJsZQ0KDQpsaWJyYXJ5KHRleHRjbGVhbikNCmh0bWxfdGFncyA8LSBjKA0KICAgICI8Ym9sZD5SYW5kb208L2JvbGQ+IHRleHQgd2l0aCBzeW1ib2xzOiAmbmJzcDsgJmx0OyAmZ3Q7ICZhbXA7ICZxdW90OyAmYXBvczsiLA0KICAgICI8cD5Nb3JlIHRleHQ8L3A+ICZjZW50OyAmcG91bmQ7ICZ5ZW47ICZldXJvOyAmY29weTsgJnJlZzsiDQopDQoNCmJsbTIwMjAkc291cmNlX3R3ZWV0PC0gcmVwbGFjZV9odG1sKGJsbTIwMjAkc291cmNlX3R3ZWV0KQ0KDQoNCmBgYA0KDQoqcnVubmluZyB0aGUgdGVzdCoNCg0KYGBge3J9DQpsaWJyYXJ5KGNhcikNCg0Kb25lLndheSA8LSBhb3Yoc291cmNlX3R3ZWV0IH4gLiwgZGF0YSA9IGJsbTIwMjApDQoNCmBgYA0KDQoNCiNHcmFwaHMgZm9yIGV2ZXJ5IHZhcmlhYmxlIA0KI0dyYXBoIG9uIHVzZXIgYW1tb3VudCBvZiBmb2xsb3dlcnMNCiNDaGVjayBpZiBhIHVzZXIgaGFzIG1hZGUgbXVsdGlwbGUgdHdlZXRzIC0gaG93IGNhbiB3ZSBkbyB0aGF0PyANCiNGaWx0ZXIgaWYgcGFydGllcyBhcmUgbWVudGlvbmVk